import { Node, director, error, log, sys, warn } from "cc";
import { NetEvent, NetManagerEvent, NetState, ReqError, SocketType } from "../../Enum/NetEnums";
import { ClientEvent } from "../bo/ClientEvent";
import ProtoEncoder from "./codec/ProtoEncoder";
import ProtoDecoder from "./codec/ProtoDecoder";
import LengthOptimizeConfig from "./codec/LengthOptimizeConfig";
import NetData from "./NetData";
import SendData from "./codec/SendData";
import { Message } from "protobufjs";

/**
 * 2023 11 01 xxkk 对cocos引擎依赖
*/
export default class BaseSocket extends Node {
  private ip: string;
  private port: number;
  private protoEncoder: ProtoEncoder; // 编码
  private protoDecoder: ProtoDecoder;// 解码工具
  private lastPingTime: number;
  status: NetState = NetState.close;
  private websocket: WebSocket | null = null;
  socketType: SocketType;
  private callbackMap: Map<number, Function> = new Map();
  private pingServerName: string = "accountService.ping";
  private timeOutInc: number = 0;
  private scheduler = director.getScheduler();// cocos定时器
  public static netPath: string = "/xxkk";

  init(ip: string, port: number, socketType: SocketType) {
    this.ip = ip;
    this.port = port;
    const len = new LengthOptimizeConfig();
    this.protoEncoder = new ProtoEncoder(len);
    this.protoDecoder = new ProtoDecoder(len, NetData.pbRoot);
    this.socketType = socketType;
  }
  /**
   * 创建并连接
   */
  connect() {
    this.status = NetState.open;
    const path = `ws://${this.ip}:${this.port}${BaseSocket.netPath}`;
    if (sys.isNative) {
      this.websocket = new WebSocket(path, null);
    } else {
      this.websocket = new WebSocket(path);
      this.websocket.binaryType = "arraybuffer";
    }

    // let connect_timeOutFun = () => {
    //   console.log("连接超时主动关闭");
    //   this.websocket.close();
    // };
    // this.scheduler.schedule(connect_timeOutFun, this, 2, 0, 2, false);

    this.websocket.onopen = () => {
      // this.scheduler.unschedule(connect_timeOutFun, this);
      this.status = NetState.open;
      console.log("成功连接服务器");
      this.emit(NetEvent.onNetOpen);
    };

    this.websocket.onmessage = (event: MessageEvent) => {
      const decoderdata = this.protoDecoder.decode(event.data);
      if (decoderdata.cmdIndex === 0) {
        console.log("服务器指令", decoderdata.serviceAndMethod, decoderdata.dataProto);
        ClientEvent.dispatchEvent(decoderdata.serviceAndMethod, decoderdata.dataProto);
      }
      else {
        const callback = this.callbackMap.get(decoderdata.cmdIndex);
        console.log("服务器回调", decoderdata.cmdIndex);
        if (callback) {
          callback(decoderdata.dataProto);
        }
        this.callbackMap.delete(decoderdata.cmdIndex);
      }
    };

    this.websocket.onerror = (event: ErrorEvent) => {
      console.log("Socket连接发生错误:", event);
      // ClientEvent.dispatchEvent(this.socketType + NetEvent.onNetError);
      this.emit(NetEvent.onNetError);
    };

    this.websocket.onclose = () => {
      this.status = NetState.close;
      console.log("Socket连接关闭");
      // ClientEvent.dispatchEvent(this.socketType + NetEvent.onNetClose);
      this.emit(NetEvent.onNetClose);
    };

  }
  /**发送请求并等待返回数据 */
  send(sendData: SendData, cb?: (event: any) => void) {
    let netError: ReqError = null;
    if (this.websocket == null) {
      console.log(ReqError.WebsocketIsNull);
      return;
    }
    if (this.websocket.readyState != WebSocket.OPEN) {
      console.log(ReqError.WebsocketIsNotOpen);
      return;
    }
    console.log("发送数据", sendData.serviceAndMethod, sendData.message);// 打印要发送的数据
    const buffer = this.protoEncoder.encode(sendData);
    // this.websocket.send(buffer);
    // console.log("设置回调打印", cb);
    if (cb != null) {
      let haveTimeOut = false;
      let timeoutFun = () => {
        haveTimeOut = true;
        console.log("请求超时：", sendData.serviceAndMethod);
        if (this.pingServerName == sendData.serviceAndMethod) {
          this.timeOutInc++;
          if (this.timeOutInc > 3) {
            console.log("ping 请求超时3次,关闭连接");
            // ClientEvent.dispatchEvent(NetState[NetState.timeout]);
            this.emit(NetEvent.onNetTimeOut);
            this.timeOutInc = 0;
            // this.websocket.close();
          }
        }
      };
      this.scheduler.schedule(timeoutFun, this, 2, 0, 2, false);
      let func = (event) => {
        if (!haveTimeOut) {
          this.scheduler.unschedule(timeoutFun, this);
        }
        this.timeOutInc = 0;// 成功接收到数据就刷新状态
        if (cb) {
          try {
            if (event && event.hasOwnProperty("errorCode")) {// 这里还没有写错误码返回的proto
              console.log("请求", sendData.serviceAndMethod, "出错");
              error(event);
            } else if (true) {// if 不是ping方法 则打印收到的数据
              console.log("服务端回调数据：", sendData.serviceAndMethod, event);
            }
            cb(event);
          } catch (error) {
            console.log("回调数据执行出错", sendData.serviceAndMethod);
            console.error(error);
          }
        }
      };
      if (this.callbackMap.has(sendData.cmdIndex)) {
        console.log("cmdIndex", sendData.cmdIndex, "已经存在");
      }
      // console.log("设置回调打印", sendData.cmdIndex, cb);
      this.callbackMap.set(sendData.cmdIndex, func);
      // console.log("所有回调数据", this.callbackMap);
    }
    this.websocket.send(buffer);
  }

  sendSync(data: SendData) {
    return new Promise<any>((resolve, reject) => {
      this.send(data, (event) => {
        resolve(event);
      });
    });
  }

  ping(): void {
    this.lastPingTime = Date.now();
    if (this.status == NetState.open && this.websocket) {
      let emptyProto = NetData.createProtoInstance("EmptyProto");
      this.send(new SendData(emptyProto, this.pingServerName));
    }
  }

  getLastPingTime(): number {
    return this.lastPingTime;
  }

  async close(): Promise<void> {// TODO 需要停止ping
    if (this.status != NetState.close && this.websocket) {
      this.websocket.close();
    }
  }
}